#! /bin/bash

if [ -z "$_FENZI_CONF_LIB" ]; then
_FENZI_CONF_LIB=0

# string stuff
#{{{
is_integer() {
#{{{
    echo "$1" | grep -q -E '^[0-9]+$'
}
#}}}

is_identifier() {
#{{{
    echo "$1" | grep -q -E '^[a-zA-Z_][a-zA-Z0-9_]*$'
}
#}}}

starts_with() {
#{{{
    echo "$1" | grep -q -e '^'"$2"'.*$'
}
#}}}

ends_with() {
#{{{
    echo "$1" | grep -q -e '^.*'"$2"'$'
}
#}}}

contains() {
#{{{
    echo "$1" | grep -q -e '^.*'"$2"'.*$'
}
#}}}

toupper() {
#{{{
    echo "$*" | tr '[:lower:]' '[:upper:]'
}
#}}}
#}}}

setvar() {
#{{{
    local con=$1

    if [ "$con" = '-' ]; then
        shift
    fi

    local var=$1 && shift
    local val="$*"

    ( [ '!' "$con" = '-'      ] || 
      [ -z "$( getvar $var )" ] ) && eval "$var=""'""$val""'"

    true
}
#}}}

getvar() {
#{{{
    local var=$1 && shift
    local ret=$1

    if [ -z "$ret" ]; then
        eval echo '$'"$var"
    else
        eval "$ret="'"$'"$var"'"'
    fi
}
#}}}

configure_file() {
#{{{
    local ifile="$1"
    local ofile="$2"

    while IFS= read -r line; do
        while [[ "$line" =~ @([^@]+)@ ]]; do
            pattern="${BASH_REMATCH[0]}"
            variable="${BASH_REMATCH[1]}"

            line=$(eval 'echo $'"{line/$pattern/$(getvar $variable)}")
        done
        echo "$line"
    done < "$ifile" > "$ofile"
}
#}}}

disp() {
#{{{
    for var in $*; do
        printf '%-20s: %s\n' "$var" "$( getvar $var )"
    done
}
#}}}

find_binary() {
#{{{
    local bin=$1
    local out=$2
    local ret=

    # if out has not been supplied...
    # make out=CAPS(bin)
    if [ -z "$out" ]; then
        out="$(toupper "$bin")"
    fi

    # if there is a file called
    # find_bin_${bin} ... use that
    if [ -f "$CONFIG_DIR/find_bin_$bin" ]; then
        source "$CONFIG_DIR/find_bin_$bin"
    fi

    # don't bother unless the file did not work out
    if [ -z "$ret" ]; then
        # try using which and keep
        # track of its return code
        which $bin &> /dev/null
        local tmp=$?

        # if which works... use it
        if [ "$tmp" -eq "0" ]; then
            ret="$(which $bin)"
        fi
    fi

    setvar $out $ret

    [ -z "$ret" ] && return 1 || return 0
}
#}}}

find_component() {
#{{{
    local com=
    for com in $*; do
        # if there is a file called
        # find_com_${com} ... use that
        if [ -f "$CONFIG_DIR/find_com_$com" ]; then
            source "$CONFIG_DIR/find_com_$com"
        else
            find_binary $com || true
        fi
    done
}
#}}}

# list data strucutre
#{{{
list() {
#{{{
    local op=$1
    shift

    eval list_$op $*
}
#}}}

list_append() {
#{{{
    local var=$1
    local arg=

    local tmp=

    shift
    for arg in $*; do
        tmp="$tmp:$arg"
    done

    if [ -z "$( getvar $var )" ]; then
        tmp="${tmp#:}"
    fi

    eval setvar $var '"${'"$var"'}'"$tmp"'"'
}
#}}}

list_push() {
#{{{
    local var=$1
    local arg=

    local tmp=

    shift
    for arg in $*; do
        tmp="$arg:$tmp"
    done

    if [ -z "$( getvar $var )" ]; then
        tmp="${tmp%:}"
    fi

    eval setvar $var '"'"$tmp"'$'"$var"'"'
}
#}}}

list_pop() {
#{{{
    local var=$1
    local ind=$2
    local ret=$3

    [[ -z "$ind" ]] && ind=0
    ind=$((ind+1))

    local tmp="$( ( getvar $var ) | cut -d : -f $ind )"

    if [[ -z "$ret" ]]; then
        echo $tmp
    else
        setvar $ret "$tmp"
    fi

    tmp="$( ( getvar $var ) | cut -d : -f $ind --complement )"
    setvar $var "$tmp"

}
#}}}

list_cat() {
#{{{
    local var=$1
    local ret=$2

    if [[ -z "$ret" ]]; then
        ( getvar "$var" ) | sed 's/:/\n/g'
    else
        tmp="$( getvar $var )"
        tmp="$( echo $tmp | sed 's/:/ /g' )"
        setvar $ret "$tmp"
    fi
}
#}}}

list_length() {
#{{{
    local var=$1
    local ret=$2
    local tmp=

    if [[ -z "$ret" ]]; then
        ( getvar "$var" ) | sed 's/:/\n/g' | wc -l
    else
        tmp="$( getvar $var )"
        tmp="$( echo $tmp | sed 's/:/\n/g' | wc -l )"
        setvar $ret "$tmp"
    fi
}
#}}}

list_get() {
#{{{
    local var=$1

    shift
    local N=$#
    local ret="${*:$N:1}"
    local ind=

    if is_identifier $ret; then
        ind="$( echo ${*:1:$((N-1))} | sed 's/ /,/g' )"
        local tmp="$( (getvar $var) | cut -d : -f $ind )"
        setvar $ret "$tmp"
    else
        ind="$( echo $*              | sed 's/ /,/g' )"
        ( getvar $var ) | cut -d : -f $ind
    fi
}
#}}}

list_uniq() {
#{{{
    local var=$1
    local ret=$2

    local i=1
    local uniq_indeces="$(
        for t in $(eval list cat $var); do
            echo $i $t
            i=$((i+1))
        done | sort -k 2 -u | cut -d ' ' -f 1 | sort )"

    if [[ -z "$ret" ]]; then
        list get $var $uniq_indeces
    else
        list get $var $uniq_indeces $ret
    fi
}
#}}}
#}}}

box_print() {
#{{{
    local off=$1 && shift
    local len=$1 && shift
    local txt="$*"
    local t=
    local tmp=
    local n=0
    local n2=0

    txt="$(echo $txt | sed 's/\s/\n/g' | sed 's/-/-\n/g')"

    if [ "$len" = '-' ]; then
        len=$(($(tput cols) - $off))
    fi

    local flag=1
    for t in $txt; do
        n2=${#t}

        if ((n+n2 >= len)); then
            if (($flag)); then
                printf "%s\n" "$tmp"
                flag=0
            else
                printf "%${off}s%s\n" '' "$tmp"
            fi
            n=0
            tmp=
        fi
        if [ -z "$tmp" ]; then
            tmp=$t
        elif ends_with "$tmp" '-'; then
            tmp="$tmp$t"
        else
            tmp="$tmp $t"
        fi
        n=${#tmp}
    done

    if [ '!' -z "$tmp" ]; then
        if (($flag)); then
            printf "%s\n" "$tmp"
        else
            printf "%${off}s%s\n" '' "$tmp"
        fi
    fi
}
#}}}

parse_command_line() {
#{{{
    local prefix="$1" && shift
    local key=
    local val=
    local argc=

    for key in $(list cat ${prefix}_list); do
        setvar "${prefix}_long_${key}" \
               "$( getvar "${prefix}_long_${key}_default" )"
    done

    while (($#)); do
        arg="$1" && shift

        if starts_with "$arg" -- ; then
            # long option
            if contains "$arg" =; then
                # long option with =
                key="$( echo $arg | cut -d = -f 1 )"
                val="$( echo $arg | cut -d = -f 2 )"
                key="${key#--}"
                argc=$(getvar "${prefix}_long_${key}_argc")
                if [ -z "$argc" ]; then
                    list append ${prefix}_args "$arg"
                    continue # no key/val to process
                fi
            else
                # long option without =
                key="${arg#--}"
                argc=$(getvar "${prefix}_long_${key}_argc")
                if [ -z "$argc" ]; then
                    list append ${prefix}_args "$arg"
                    continue # no key/val to process
                fi
                if (($argc)); then
                    # long compound option
                    val="$1" && shift
                else
                    # long flag option
                    val=1
                fi
            fi

        elif starts_with "$arg" - ; then
            # short option
            key="${arg#-}"
            key=$(getvar "${prefix}_short_${key}")
            if [ -z "$key" ]; then
                list append ${prefix}_args "$arg"
                continue # no key/val to process
            fi
            argc=$(getvar "${prefix}_long_${key}_argc")
            if (($argc)); then
                # long compound option
                val="$1" && shift
            else
                # long flag option
                val=1
            fi

        else
            # regular option
            list append ${prefix}_args "$arg"
            continue # no key/val to process
        fi

        list append ${prefix}_list "$key"
        setvar "${prefix}_long_$key" "$val"
    done

    list uniq "${prefix}_list" "${prefix}_list"
}
#}}}

opt_print() {
#{{{
    local prefix="$1" && shift
    local key=

    for key in $(list cat ${prefix}_list); do
        printf "%-20s: %s\n" "$key" "$( getvar "${prefix}_long_${key}" )"
    done

    echo
    echo "unnamed args:"
    echo
    for key in $(list cat ${prefix}_args); do
        echo "	$key"
    done
}
#}}}

add_opt() {
#{{{
    local prefix=$1  && shift
    local group=$1   && shift
    local opt=$1     && shift
    local short=$1   && shift
    local argc=$1    && shift
    local default=$1 && shift
    local doc="$*"

    list append ${prefix}_list          $opt
    list append ${prefix}_group_list    $group
    list append ${prefix}_${group}_list $opt
    list uniq   ${prefix}_group_list    ${prefix}_group_list

    if [ '!' -z "$short" ]; then
        setvar "${prefix}_long_${opt}_short" "$short"
        setvar "${prefix}_short_$short"      "$opt"
    fi

    setvar "${prefix}_long_${opt}_argc"    "$argc"
    setvar "${prefix}_long_${opt}_default" "$default"
    setvar "${prefix}_long_${opt}_doc"     "$doc"
}
#}}}

usage() {
#{{{
    local prefix="$1" && shift
    local key=
    local group=

    for group in $(list cat ${prefix}_group_list); do
        echo "$group:"
        for key in $(list cat ${prefix}_${group}_list); do
            local short="$( getvar "${prefix}_long_${key}_short" )"
            local doc="$( getvar "${prefix}_long_${key}_doc" )"
            local default="$( getvar "${prefix}_long_${key}_default" )"

            [ -z "$short" ] && echo -n "       " || echo -n "  -$short   "
            [ '!' -z "$default" ] && default="[${default:0:17}]"
            printf "%-9s  %-19s "    \
                   '--'"${key:0:7}"  \
                   "${default:0:19}"

            box_print 38 - "$doc"
        done
        echo
    done

}
#}}}

fi

